package com.igbeok.hibernate.base.dao;

import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.igbeok.hibernate.common.orm.hibernate.transform.CustomTransformers;

public class BaseDao {
	Logger log = LoggerFactory.getLogger(getClass());
	
	@Resource
	private SessionFactory sessionFactory;

	public BaseDao() {}

	public BaseDao(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}


	/**
	 * native SQL 查询, 结果集返回bean list
	 * 注意sql语句中包含的字段在class类中必须有相应的属性, 否则会报错.
	 * 
	 * @param <Y>
	 * @param cls 要返回的list中包含对象的Class对象
	 * @param sqlStr 查询的SQL语句
	 * @param params 查询参数
	 * @return 包含参数cls指定的类的对象的list
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryForList(Class<T> cls, String sqlStr, Map<String, ?> params) {
		SQLQuery query = (SQLQuery) createSqlQuery(cls, sqlStr, params);
		return query.list();
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> queryForList(Class<T> cls, String sqlStr, String paramName, Object value) {
		SQLQuery query = (SQLQuery) createSqlQuery(cls, sqlStr, paramName, value);
		return query.list();
	}
	

	/**
	 * SQL语句写在xml文件里的native SQL 查询, 结果集返回bean list
	 * 注意sql语句中包含的字段在class类中必须有相应的属性, 否则会报错.
	 * 例:
	 * 	<sql-query name="getStudentInfo2">
	 * 		<![CDATA[
	 * 		select s.*, z.city from student s join zipcode z on s.zip = z.zip 
     * 	   WHERE s.student_id>:student_id order by student_id
	 * 		]]>
	 * 	</sql-query>
	 * 
	 * @param <Y>
	 * @param cls 要返回的list中包含的对象的Class对象
	 * @param queryName xml中定义的查询的名字
	 * @param params
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> namedQueryForList(Class<T> cls, String queryName, Map<String, ?> params) {
		SQLQuery query = (SQLQuery) createNamedSqlQuery(cls, queryName, params);
		return query.list();
	}
	
	/**
	 * native SQL 查询, 结果集返回bean list
	 * 注意sql语句中包含的字段在class类中必须有相应的属性, 否则会报错.
	 * 支持分页的版本, 最后生成的分页sql语句where条件如下:
	 * where rownum_ <= 20 and rownum_ > 10
	 * 所以要从第11条记录开始取的话, start应为10,这样可能会为客户端查询带来误解
	 * 这边人为的将start-1
	 * 
	 * @param <Y>
	 * @param cls
	 * @param sqlStr
	 * @param params
	 * @param start 从第几条记录开始查询, 计数从1开始
	 * @param maxsize 每次取几条
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> queryForPagedList(Class<T> cls, String sqlStr, Map<String, ?> params, int start, int maxsize) {
		start = start - 1 < 0 ? 0 : start - 1;
		SQLQuery query = (SQLQuery) createSqlQuery(cls, sqlStr, params).setFirstResult(start).setMaxResults(maxsize);
		return query.list();
	}
	

	
	/**
	 * SQL语句写在xml文件里的native SQL 查询, 结果集返回bean list
	 * 注意sql语句中包含的字段在class类中必须有相应的属性, 否则会报错.
	 * 支持分页的版本, 最后生成的分页sql语句where条件如下:
	 * where rownum_ <= 20 and rownum_ > 10
	 * 所以要从第11条记录开始取的话, start应为10,这样可能会为客户端查询带来误解
	 * 这边人为的将start-1
	 * 例:
	 * 	<sql-query name="getStudentInfo2">
	 * 		<![CDATA[
	 * 		select s.*, z.city from student s join zipcode z on s.zip = z.zip 
     * 	   WHERE s.student_id>:student_id order by student_id
	 * 		]]>
	 * 	</sql-query>
	 * 
	 * @param <Y>
	 * @param cls
	 * @param queryName 查询的名称
	 * @param params
	 * @param start 从第几条记录开始查询, 计数从1开始
	 * @param maxsize 每次取几条
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> List<T> namedQueryForPagedList(Class<T> cls, String queryName, Map<String, ?> params, int start, int maxsize) {
		start = start - 1 < 0 ? 0 : start - 1;
		SQLQuery query = (SQLQuery) createNamedSqlQuery(cls, queryName, params).setFirstResult(start).setMaxResults(maxsize);
		return query.list();
	}
	

	/**
	 * 查询单个对象
	 * @param <T>
	 * @param cls 要返回对象的Class对象
	 * @param sqlStr 查询的SQL语句
	 * @param params 查询参数
	 * @return
	 */
	public <T> T queryForObject(Class<T> cls, String sqlStr, Map<String, ?> params) {
		List<T> result = queryForList(cls, sqlStr, params);
		if (!result.isEmpty()) {
			return result.get(0);
		}
		return null;
	}
	
	public <T> T queryForObject(Class<T> cls, String sqlStr, String paramName, Object value) {
		List<T> result = queryForList(cls, sqlStr, paramName, value);
		if (!result.isEmpty()) {
			return result.get(0);
		}
		return null;
	}
	
	/**
	 * SQL语句写在xml文件里的native SQL 查询单个对象
	 * @param <T>
	 * @param cls 要返回对象的Class对象
	 * @param queryName 查询的名称
	 * @param params
	 * @return
	 */
	public <T> T namedQueryForObject(Class<T> cls, String queryName, Map<String, ?> params) {
		List<T> result = namedQueryForPagedList(cls, queryName, params, 1, 1);
		if (!result.isEmpty()) {
			return result.get(0);
		}
		return null;
	}
	

	/**
	 * 创建一个SQLQuery对象 该SQLQuery对象返回的结果集中包含的是Class target所代表的javabean对象
	 * 
	 * @param sqlStr
	 * @param params
	 * @return
	 */
	private SQLQuery createSqlQuery(Class<?> target, String sqlStr, Map<String, ?> params) {
		SQLQuery query = createSqlQuery(sqlStr, params);
		query.setResultTransformer(CustomTransformers.underlineStyleTransformer(target));
		return query;
	}
	
	private SQLQuery createSqlQuery(Class<?> target, String sqlStr, String paramName, Object value) {
		SQLQuery query = getSession().createSQLQuery(sqlStr);
		query.setParameter(paramName, value);
		query.setResultTransformer(CustomTransformers.underlineStyleTransformer(target));
		return query;
	}

	/**
	 * 创建一个SQLQuery对象 该SQLQuery对象返回的结果集中包含的是Object[]
	 * 
	 * @param sqlStr
	 * @param params
	 * @return
	 */
	private SQLQuery createSqlQuery(String sqlStr, Map<String, ?> params) {
		SQLQuery query = getSession().createSQLQuery(sqlStr);
		setQueryParam(params, query);
		return query;
	}


	/**
	 * 绑定查询参数
	 * 
	 * @param params
	 * @param query
	 */
	private void setQueryParam(Map<String, ?> params, Query query) {
		if (null == params)
			return;
		for (String key : params.keySet()) {
			query.setParameter(key, params.get(key));
		}
	}

	/**
	 * 获取当前session
	 * 
	 * @return
	 */
	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}


	/**
	 * 创建一个SQLQuery对象 该SQLQuery对象返回的结果集中包含的是Class target所代表的javabean对象
	 * 
	 * @param sqlStr
	 * @param params
	 * @return
	 */
	private SQLQuery createNamedSqlQuery(Class<?> target, String queryName, Map<String, ?> params) {
		SQLQuery query = (SQLQuery)createNamedSqlQuery(queryName, params);
		query.setResultTransformer(CustomTransformers.underlineStyleTransformer(target));
		return query;
	}


	/**
	 * 创建一个SQLQuery对象 该SQLQuery对象返回的结果集中包含的是Object[]
	 * 
	 * @param sqlStr
	 * @param params
	 * @return
	 */
	private Query createNamedSqlQuery(String queryName, Map<String, ?> params) {
		Query query = getSession().getNamedQuery(queryName);
		setQueryParam(params, query);
		return query;
	}


}
